package com.dinim.object;

import java.awt.Color;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

import com.dinim.graphics.IDrawable;
import com.dinim.graphics.IGraphicsContext;
import com.dinim.math.GeometryUtil;
import com.dinim.matrix.Matrix;
import com.dinim.matrix.twodimensional.IPoint2D;
import com.dinim.matrix.twodimensional.Matrix3x3;
import com.dinim.matrix.twodimensional.Vector3;
import com.dinim.physics2D.BoundingBox2D;
import com.dinim.physics2D.IBoundingBox2D;

public abstract class Object2D implements ITransformable, IObject2D, IDrawable{

    private IPoint2D[] points;
    private int[] connections;
    
    public Object2D(IPoint2D[] points, int[] connections) {
        this.points = points;
        this.connections = connections;
    }

    public int[] getConnections() {
        return connections;
    }

    public void setConnections(int[] connections) {
        this.connections = connections;
    }

    public IPoint2D[] getPoints() {
        return points;
    }

    public void setPoints(IPoint2D[] points) {
        this.points = points;
    }

    public IPoint2D getCenterPoint() {
        IBoundingBox2D bb = getBoundingBox();
        double halfwidth = (bb.getRight() - bb.getLeft()) / 2;
        double halfheight = (bb.getBottom() - bb.getTop()) / 2;
        
        return new Point2D(bb.getLeft() + halfwidth, bb.getTop() + halfheight);
    }

    public void draw(IGraphicsContext gc){
        IPoint2D p1 = getCenterPoint();
        for(int i = 0; i < connections.length; i++){
            int x1 = (int)points[connections[i]].getX();
            int y1 = (int)points[connections[i]].getY();
            int x2;
            int y2;
            Line2D normal = null;
            if ((i + 1) >= connections.length) {
                x2 = (int)points[connections[0]].getX();
                y2 = (int)points[connections[0]].getY();
                normal = new Line2D(points[connections[i]], points[connections[0]]);
            } else {
                x2 = (int)points[connections[i + 1]].getX();
                y2 = (int)points[connections[i + 1]].getY();
                normal = new Line2D(points[connections[i]], points[connections[i + 1]]);
            }
            
//            gc.setColor(Color.green);
//            gc.drawLine(p1, new Point2D(x1, y1));
//            gc.drawLine(p1, normal.getCenterPoint());
//            gc.setColor(Color.black);
//            
//            System.out.println(GeometryUtil.getGradient(p1.getX(), p1.getY(), x2, y2));
//            System.out.println(GeometryUtil.getGradient(p1.getX(), p1.getY(), normal.getCenterPoint().getX(), normal.getCenterPoint().getY()));
//            
//            Line2D normal2 = new Line2D(new Point2D(normal.getPoints()[0].getX(), normal.getPoints()[0].getY()), new Point2D(normal.getPoints()[1].getX(), normal.getPoints()[1].getY()));
//            normal2.rotate(90, normal.getCenterPoint());
//            gc.drawLine(normal2.getPoints()[0].getX(), normal2.getPoints()[0].getY(), normal2.getPoints()[1].getX(), normal2.getPoints()[1].getY());
            gc.drawText(i+"", x1, y1);
            gc.drawLine(x1, y1, x2, y2);
        }
    }

    public void rotate(double angle, IPoint2D referencepoint) {
        Vector3 vector = new Vector3();
        for(int i = 0; i < points.length; i++){
            vector.setX(points[i].getX());
            vector.setY(points[i].getY());
            Matrix translation = Matrix3x3.generateTranslationMatrixXY(-referencepoint.getX(), -referencepoint.getY());
            vector.multiplyByMatrix(translation);
            Matrix rotation = Matrix3x3.generateRotationMatrix(angle);
            vector.multiplyByMatrix(rotation);
            translation = Matrix3x3.generateTranslationMatrixXY(referencepoint.getX(), referencepoint.getY());
            vector.multiplyByMatrix(translation);
            points[i].setX(vector.getX());
            points[i].setY(vector.getY());
        }
    }

    public void scale(double x, double y, IPoint2D referencepoint) {
        Vector3 vector = new Vector3();
        for(int i = 0; i < points.length; i++){
            vector.setX(points[i].getX());
            vector.setY(points[i].getY());
            Matrix translation = Matrix3x3.generateTranslationMatrixXY(-referencepoint.getX(), -referencepoint.getY());
            vector.multiplyByMatrix(translation);
            Matrix scaling = Matrix3x3.generateScalingMatrixXY(x, y);
            vector.multiplyByMatrix(scaling);
            translation = Matrix3x3.generateTranslationMatrixXY(referencepoint.getX(), referencepoint.getY());
            vector.multiplyByMatrix(translation);
            points[i].setX(vector.getX());
            points[i].setY(vector.getY());
        }
    }

    public void translate(double x, double y) {
        Vector3 vector = new Vector3();
        for(int i = 0; i < points.length; i++){
            vector.setX(points[i].getX());
            vector.setY(points[i].getY());
            Matrix translation = Matrix3x3.generateTranslationMatrixXY(x, y);
            vector.multiplyByMatrix(translation);
            points[i].setX(vector.getX());
            points[i].setY(vector.getY());
        }
    }

    public void translate(int objectPointIdx, IPoint2D toPoint){
        double x = toPoint.getX() - points[objectPointIdx].getX();
        double y = toPoint.getY() - points[objectPointIdx].getY();
        Vector3 vector = new Vector3();
        for(int i = 0; i < points.length; i++){
            vector.setX(points[i].getX());
            vector.setY(points[i].getY());
            Matrix translation = Matrix3x3.generateTranslationMatrixXY(x, y);
            vector.multiplyByMatrix(translation);
            points[i].setX(vector.getX());
            points[i].setY(vector.getY());
        }
    }
    
    public IBoundingBox2D getBoundingBox() {
        if(points.length <= 1){
            return null;
        }
        if(this instanceof Line2D){
            //System.out.println("jap");
        }
        IBoundingBox2D bb = new BoundingBox2D(points[0].getX(), points[0].getY(), 
                                              points[0].getX(), points[0].getY());
        for(int i = 1; i < points.length; i++){
            if(bb.getLeft() > points[i].getX()){
                bb.setLeft(points[i].getX());
            }
            if(bb.getTop() > points[i].getY()){
                bb.setTop(points[i].getY());
            }
            if(bb.getRight() < points[i].getX()){
                bb.setRight(points[i].getX());
            }
            if(bb.getBottom() < points[i].getY()){
                bb.setBottom(points[i].getY());
            }
        }
        if(bb.getTop() > bb.getBottom()){
        	throw new RuntimeException("fuckingshit!!");
        }
        if(bb.getLeft() > bb.getRight()){
        	throw new RuntimeException("fuckoffyeah!!!");
        }

        return bb;
    }

    public List getLineBoundingBoxes(int splitting) {
        List boundingboxes = new ArrayList(connections.length * splitting);
        
        for(int i = 0; i < connections.length; i++){
//            int x1 = (int)Math.round(points[connections[i]].getX());
//            int y1 = (int)Math.round(points[connections[i]].getY());
//            int x2;
//            int y2;
//            if ((i + 1) >= connections.length) {
//                x2 = (int)Math.round(points[connections[0]].getX());
//                y2 = (int)Math.round(points[connections[0]].getY());
//            } else {
//                x2 = (int)Math.round(points[connections[i + 1]].getX());
//                y2 = (int)Math.round(points[connections[i + 1]].getY());
//            }
            double x1 = points[connections[i]].getX();
            double y1 = points[connections[i]].getY();
            double x2;
            double y2;
            if ((i + 1) >= connections.length) {
                x2 = points[connections[0]].getX();
                y2 = points[connections[0]].getY();
            } else {
                x2 = points[connections[i + 1]].getX();
                y2 = points[connections[i + 1]].getY();
            }
            double width = (x2 - x1) / splitting;
            double height = (y2 - y1) / splitting;
            for(int j = 0; j < splitting; j++){
                double x = x1 + width * j;
                double y = y1 + height * j;
                IBoundingBox2D box = new BoundingBox2D(x, y, x + width, y + height);
                if(box.getTop() > box.getBottom()){
                	//throw new RuntimeException("fuckingshit!!");
                	System.out.println("yup");
                }
                if(box.getLeft() > box.getRight()){
                	//throw new RuntimeException("fuckoffyeah!!!");
                	System.out.println("yup");
                }
                boundingboxes.add(box);
            }
        }
        return boundingboxes;
    }

    public List getLines() {
        List lines = new ArrayList(connections.length);
        for(int i = 0; i < connections.length; i++){
            IPoint2D p1 = points[connections[i]];
            IPoint2D p2;
            if ((i + 1) >= connections.length) {
                p2 = points[connections[0]];
            } else {
                p2 = points[connections[i+1]];
            }
            lines.add(new Line2D(p1, p2));
        }
        return lines;
    }
    
    public void drawNormals(IGraphicsContext gc){
        List normals = getNormals();
        for(int i = 0; i < normals.size(); i++){
            Line2D normal = (Line2D) normals.get(i);
            gc.drawLine(normal.getPoints()[0], normal.getPoints()[1]);
        }
    }
    
    public List getNormals(){
        IPoint2D cp = getCenterPoint();
        List normals = new ArrayList();
        List lines = getLines();
        for(int i = 0; i < lines.size(); i++){
            Line2D normal = new Line2D(((Line2D)lines.get(i)).getX1(), ((Line2D)lines.get(i)).getY1(), ((Line2D)lines.get(i)).getX2(), ((Line2D)lines.get(i)).getY2());
            normal.rotate(90, normal.getCenterPoint());
            Line2D normal2 = new Line2D(((Line2D)lines.get(i)).getX1(), ((Line2D)lines.get(i)).getY1(), cp.getX(), cp.getY());
            normals.add(normal);
            normals.add(normal2);
        }
        return normals;
    }
    
    public List getNormalGradients(){
        List lines = getNormals();
        List gradients = new ArrayList();
        for(int i = 0; i < lines.size(); i++){
            Line2D line = (Line2D) lines.get(i);
            double gradient = GeometryUtil.getGradient(line.getX1(), line.getY1(), line.getX2(), line.getY2());
            gradients.add(gradient);
        }
        Collections.sort(gradients);
        return gradients;
    }
    
}
